﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;

namespace AgileShare.Framework.Library.Helpers
{
    public static class SPImpersonationHelper
    {
        public delegate void SPSiteAction(SPSite site);
        public delegate void SPWebAction(SPWeb web);
        public delegate void SPListAction(SPList list);
        /// <summary>
        /// Performs an action using an SPSite object impersonating the SystemUser for the site
        /// </summary>
        public static void DoAsSystemUser(this SPSite site, SPSiteAction action)
        {
            var systemUser = site.SystemAccount;
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                DoAsUser(site, systemUser, action);
            });
        }

        /// <summary>
        /// Performs an action using an SPWeb object impersonating the SystemUser for the site
        /// </summary>
        public static void DoAsSystemUser(this SPWeb web, SPWebAction action)
        {
            SPUser systemUser = web.Site.SystemAccount;
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                DoAsUser(web, systemUser, action);
            });
        }

        /// <summary>
        /// Performs an action using an SPWeb object impersonating a given user and
        /// allowing unsafe updates
        /// </summary>
        public static void DoUnsafeUpdateAsSystemUser(this SPWeb web, SPWebAction action)
        {
            web.DoAsSystemUser(delegate(SPWeb systemWeb)
            {
                systemWeb.DoUnsafeUpdate(delegate()
                {
                    action(systemWeb);
                });
            });
        }


        /// <summary>
        /// Performs an action using an SPWeb object that requires unsafe updates
        /// </summary>
        public static void DoUnsafeUpdate(this SPWeb web, Action action)
        {
            bool allowUnsafeUpdates = web.AllowUnsafeUpdates;
            web.AllowUnsafeUpdates = true;
            action();
            web.AllowUnsafeUpdates = allowUnsafeUpdates;
        }

        /// <summary>
        /// Performs an action using an SPSite object impersonating a given user
        /// </summary>
        public static void DoAsUser(this SPSite site, SPUser user, SPSiteAction action)
        {
            DoAsUser(site, user, delegate(SPSite impersonatedSite, bool isNewSite)
            {
                action(impersonatedSite);
            });
        }

        /// <summary>
        /// Performs an action using an SPWeb object impersonating a given user
        /// </summary>
        public static void DoAsUser(this SPWeb web, SPUser user, SPWebAction action)
        {
            DoAsUser(web.Site, user, delegate(SPSite impersonatedSite, bool isNewSite)
            {
                if (!isNewSite)
                {
                    action(web);
                    return;
                }

                using (var impersonatedWeb = impersonatedSite.OpenWeb(web.ID))
                {
                    action(impersonatedWeb);
                }
            });
        }

        private delegate void SPSiteActionInternal(SPSite site, bool isNewSite);
        private static void DoAsUser(SPSite site, SPUser user, SPSiteActionInternal action)
        {
            // If we are already running as the given User Token, just pass the web along
            if (site.UserToken != null && site.UserToken.CompareUser(user.UserToken))
            {
                action(site, false);
                return;
            }

            using (var impersonatedSite = new SPSite(site.ID, user.UserToken))
            {
                action(site, true);
            }
        }

    }
}
